//////////////////////////////////////////////////////////////////////
////                                                              ////
////  syncreg.v                                                   ////
////                                                              ////
////                                                              ////
////  Synchronizes a register between two clock domains           ////
////                                                              ////
////  Author(s):                                                  ////
////       Nathan Yawn (nathan.yawn@opencores.org)                ////
////                                                              ////
////                                                              ////
////                                                              ////
//////////////////////////////////////////////////////////////////////
////                                                              ////
//// Copyright (C) 2010 Authors                                   ////
////                                                              ////
//// This source file may be used and distributed without         ////
//// restriction provided that this copyright statement is not    ////
//// removed from the file and that any derivative work contains  ////
//// the original copyright notice and the associated disclaimer. ////
////                                                              ////
//// This source file is free software; you can redistribute it   ////
//// and/or modify it under the terms of the GNU Lesser General   ////
//// Public License as published by the Free Software Foundation; ////
//// either version 2.1 of the License, or (at your option) any   ////
//// later version.                                               ////
////                                                              ////
//// This source is distributed in the hope that it will be       ////
//// useful, but WITHOUT ANY WARRANTY; without even the implied   ////
//// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR      ////
//// PURPOSE.  See the GNU Lesser General Public License for more ////
//// details.                                                     ////
////                                                              ////
//// You should have received a copy of the GNU Lesser General    ////
//// Public License along with this source; if not, download it   ////
//// from http://www.opencores.org/lgpl.shtml                     ////
////                                                              ////
//////////////////////////////////////////////////////////////////////
//
// This is a synchronization element between two clock domains. Domain A
// is considered the 'source' domain (produces the data), and Domain B
// is considered the 'destination' domain (consumes the data).  It is assumed
// that clock A is faster than clock B, but this element will work
// regardless.  The idea here is NOT to insure that domain B sees every
// change to the value generated by domain A.  Rather, this device
// attempts to keep the value seen by domain B as current as possible,
// always updating to the latest value of the input in domain A.
// Thus, there may be dozens or hundreds of changes to register A
// which are not seen by domain B.  There is no external acknowledge
// of receipt from domain B.  Domain B simply wants the most current
// value of register A possible at any given time.
// Note the reset is asynchronous; this is necessary to coordinate between
// two clock domains which may have separate reset signals.  I could find
// no other way to insure correct initialization with two separate 
// reset signals.
//
// Ports:
// CLKA:  Clock for the source domain
// CLKB:  Clock for the destination domain
// RST:  Asynchronously resets all sync elements, prepares
//       unit for operation.
// DATA_IN:  Data input from clock domain A
// DATA_OUT: Data output to clock domain B
// 


// Top module
module syncreg (
                CLKA,
		CLKB,
		RSTN,
                DATA_IN,
                DATA_OUT
		);


   input   CLKA;
   input   CLKB;
   input   RSTN;
   input   [3:0] DATA_IN;
   output  [3:0] DATA_OUT;

   reg 	   [3:0] regA;
   reg 	   [3:0] regB;
   reg 	   strobe_toggle;
   reg 	   ack_toggle;
   
   wire    A_not_equal;
   wire    A_enable;
   wire    strobe_sff_out;
   wire    ack_sff_out;
   wire [3:0]   DATA_OUT;

   // Combinatorial assignments
   assign  A_enable = A_not_equal & ack_sff_out;
   assign  A_not_equal = !(DATA_IN == regA);
   assign DATA_OUT = regB;   
 	   
   // register A (latches input any time it changes)
   always @ (posedge CLKA or negedge RSTN)
     begin
	if(~RSTN)
	  regA <= 4'b0;
	else if(A_enable)
	  regA <= DATA_IN;
     end

	   
   // register B (latches data from regA when enabled by the strobe SFF)
   always @ (posedge CLKB or negedge RSTN)
     begin
	if(~RSTN)
	  regB <= 4'b0;
	else if(strobe_sff_out)
	  regB <= regA;
     end

	   
   // 'strobe' toggle FF
   always @ (posedge CLKA or negedge RSTN)
     begin
	if(~RSTN)
	  strobe_toggle <= 1'b0;
	else if(A_enable)
	  strobe_toggle <= ~strobe_toggle;
     end

   
   // 'ack' toggle FF
   // This is set to '1' at reset, to initialize the unit.
   always @ (posedge CLKB or negedge RSTN)
     begin
	if(~RSTN)
	  ack_toggle <= 1'b1;
	else if (strobe_sff_out)
	  ack_toggle <= ~ack_toggle;
     end
   
   // 'strobe' sync element
   syncflop strobe_sff (
			.DEST_CLK (CLKB),
			.D_SET (1'b0),
			.D_RST (strobe_sff_out),
			.RESETN (RSTN),
			.TOGGLE_IN (strobe_toggle),
			.D_OUT (strobe_sff_out)
			);
 
   // 'ack' sync element
   syncflop ack_sff (
		     .DEST_CLK (CLKA),
		     .D_SET (1'b0),
		     .D_RST (A_enable),
		     .RESETN (RSTN),
		     .TOGGLE_IN (ack_toggle),
		     .D_OUT (ack_sff_out)
		     );  
endmodule
